/*
 * pgp-pubkey.c
 *	  读取公钥或私钥。
 *
 * Copyright (c) 2005 Marko Kreen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * contrib/pgcrypto/pgp-pubkey.c
 */
#include "postgres.h"

#include "mbuf.h"
#include "pgp.h"
#include "px.h"

int pgp_key_alloc(PGP_PubKey **fc_pk_p)
{
	PGP_PubKey *fc_pk;

	fc_pk = palloc0(sizeof(*fc_pk));
	*fc_pk_p = fc_pk;
	return 0;
}

void pgp_key_free(PGP_PubKey *fc_pk)
{
	if (fc_pk == NULL)
		return;

	switch (fc_pk->algo)
	{
		case PGP_PUB_ELG_ENCRYPT:
			pgp_mpi_free(fc_pk->pub.elg.p);
			pgp_mpi_free(fc_pk->pub.elg.g);
			pgp_mpi_free(fc_pk->pub.elg.y);
			pgp_mpi_free(fc_pk->sec.elg.x);
			break;
		case PGP_PUB_RSA_SIGN:
		case PGP_PUB_RSA_ENCRYPT:
		case PGP_PUB_RSA_ENCRYPT_SIGN:
			pgp_mpi_free(fc_pk->pub.rsa.n);
			pgp_mpi_free(fc_pk->pub.rsa.e);
			pgp_mpi_free(fc_pk->sec.rsa.d);
			pgp_mpi_free(fc_pk->sec.rsa.p);
			pgp_mpi_free(fc_pk->sec.rsa.q);
			pgp_mpi_free(fc_pk->sec.rsa.u);
			break;
		case PGP_PUB_DSA_SIGN:
			pgp_mpi_free(fc_pk->pub.dsa.p);
			pgp_mpi_free(fc_pk->pub.dsa.q);
			pgp_mpi_free(fc_pk->pub.dsa.g);
			pgp_mpi_free(fc_pk->pub.dsa.y);
			pgp_mpi_free(fc_pk->sec.dsa.x);
			break;
	}
	px_memset(fc_pk, 0, sizeof(*fc_pk));
	pfree(fc_pk);
}

static int fc_calc_key_id(PGP_PubKey *fc_pk)
{
	int			fc_res;
	PX_MD	   *fc_md;
	int			fc_len;
	uint8		fc_hdr[3];
	uint8		fc_hash[20];

	fc_res = pgp_load_digest(PGP_DIGEST_SHA1, &fc_md);
	if (fc_res < 0)
		return fc_res;

	fc_len = 1 + 4 + 1;
	switch (fc_pk->algo)
	{
		case PGP_PUB_ELG_ENCRYPT:
			fc_len += 2 + fc_pk->pub.elg.p->bytes;
			fc_len += 2 + fc_pk->pub.elg.g->bytes;
			fc_len += 2 + fc_pk->pub.elg.y->bytes;
			break;
		case PGP_PUB_RSA_SIGN:
		case PGP_PUB_RSA_ENCRYPT:
		case PGP_PUB_RSA_ENCRYPT_SIGN:
			fc_len += 2 + fc_pk->pub.rsa.n->bytes;
			fc_len += 2 + fc_pk->pub.rsa.e->bytes;
			break;
		case PGP_PUB_DSA_SIGN:
			fc_len += 2 + fc_pk->pub.dsa.p->bytes;
			fc_len += 2 + fc_pk->pub.dsa.q->bytes;
			fc_len += 2 + fc_pk->pub.dsa.g->bytes;
			fc_len += 2 + fc_pk->pub.dsa.y->bytes;
			break;
	}

	fc_hdr[0] = 0x99;
	fc_hdr[1] = fc_len >> 8;
	fc_hdr[2] = fc_len & 0xFF;
	px_md_update(fc_md, fc_hdr, 3);

	px_md_update(fc_md, &fc_pk->ver, 1);
	px_md_update(fc_md, fc_pk->time, 4);
	px_md_update(fc_md, &fc_pk->algo, 1);

	switch (fc_pk->algo)
	{
		case PGP_PUB_ELG_ENCRYPT:
			pgp_mpi_hash(fc_md, fc_pk->pub.elg.p);
			pgp_mpi_hash(fc_md, fc_pk->pub.elg.g);
			pgp_mpi_hash(fc_md, fc_pk->pub.elg.y);
			break;
		case PGP_PUB_RSA_SIGN:
		case PGP_PUB_RSA_ENCRYPT:
		case PGP_PUB_RSA_ENCRYPT_SIGN:
			pgp_mpi_hash(fc_md, fc_pk->pub.rsa.n);
			pgp_mpi_hash(fc_md, fc_pk->pub.rsa.e);
			break;
		case PGP_PUB_DSA_SIGN:
			pgp_mpi_hash(fc_md, fc_pk->pub.dsa.p);
			pgp_mpi_hash(fc_md, fc_pk->pub.dsa.q);
			pgp_mpi_hash(fc_md, fc_pk->pub.dsa.g);
			pgp_mpi_hash(fc_md, fc_pk->pub.dsa.y);
			break;
	}

	px_md_finish(fc_md, fc_hash);
	px_md_free(fc_md);

	memcpy(fc_pk->key_id, fc_hash + 12, 8);
	px_memset(fc_hash, 0, 20);

	return 0;
}

int _pgp_read_public_key(PullFilter *fc_pkt, PGP_PubKey **fc_pk_p)
{
	int			fc_res;
	PGP_PubKey *fc_pk;

	fc_res = pgp_key_alloc(&fc_pk);
	if (fc_res < 0)
		return fc_res;

	/* 获取版本 */
	GETBYTE(fc_pkt, fc_pk->ver);
	if (fc_pk->ver != 4)
	{
		fc_res = PXE_PGP_NOT_V4_KEYPKT;
		goto out;
	}

	/* 读取时间 */
	fc_res = pullf_read_fixed(fc_pkt, 4, fc_pk->time);
	if (fc_res < 0)
		goto out;

	/* 公钥算法 */
	GETBYTE(fc_pkt, fc_pk->algo);

	switch (fc_pk->algo)
	{
		case PGP_PUB_DSA_SIGN:
			fc_res = pgp_mpi_read(fc_pkt, &fc_pk->pub.dsa.p);
			if (fc_res < 0)
				break;
			fc_res = pgp_mpi_read(fc_pkt, &fc_pk->pub.dsa.q);
			if (fc_res < 0)
				break;
			fc_res = pgp_mpi_read(fc_pkt, &fc_pk->pub.dsa.g);
			if (fc_res < 0)
				break;
			fc_res = pgp_mpi_read(fc_pkt, &fc_pk->pub.dsa.y);
			if (fc_res < 0)
				break;

			fc_res = fc_calc_key_id(fc_pk);
			break;

		case PGP_PUB_RSA_SIGN:
		case PGP_PUB_RSA_ENCRYPT:
		case PGP_PUB_RSA_ENCRYPT_SIGN:
			fc_res = pgp_mpi_read(fc_pkt, &fc_pk->pub.rsa.n);
			if (fc_res < 0)
				break;
			fc_res = pgp_mpi_read(fc_pkt, &fc_pk->pub.rsa.e);
			if (fc_res < 0)
				break;

			fc_res = fc_calc_key_id(fc_pk);

			if (fc_pk->algo != PGP_PUB_RSA_SIGN)
				fc_pk->can_encrypt = 1;
			break;

		case PGP_PUB_ELG_ENCRYPT:
			fc_res = pgp_mpi_read(fc_pkt, &fc_pk->pub.elg.p);
			if (fc_res < 0)
				break;
			fc_res = pgp_mpi_read(fc_pkt, &fc_pk->pub.elg.g);
			if (fc_res < 0)
				break;
			fc_res = pgp_mpi_read(fc_pkt, &fc_pk->pub.elg.y);
			if (fc_res < 0)
				break;

			fc_res = fc_calc_key_id(fc_pk);

			fc_pk->can_encrypt = 1;
			break;

		default:
			px_debug("unknown public algo: %d", fc_pk->algo);
			fc_res = PXE_PGP_UNKNOWN_PUBALGO;
	}

out:
	if (fc_res < 0)
		pgp_key_free(fc_pk);
	else
		*fc_pk_p = fc_pk;

	return fc_res;
}

#define HIDE_CLEAR 0
#define HIDE_CKSUM 255
#define HIDE_SHA1 254

static int fc_check_key_sha1(PullFilter *fc_src, PGP_PubKey *fc_pk)
{
	int			fc_res;
	uint8		fc_got_sha1[20];
	uint8		fc_my_sha1[20];
	PX_MD	   *fc_md;

	fc_res = pullf_read_fixed(fc_src, 20, fc_got_sha1);
	if (fc_res < 0)
		return fc_res;

	fc_res = pgp_load_digest(PGP_DIGEST_SHA1, &fc_md);
	if (fc_res < 0)
		goto err;
	switch (fc_pk->algo)
	{
		case PGP_PUB_ELG_ENCRYPT:
			pgp_mpi_hash(fc_md, fc_pk->sec.elg.x);
			break;
		case PGP_PUB_RSA_SIGN:
		case PGP_PUB_RSA_ENCRYPT:
		case PGP_PUB_RSA_ENCRYPT_SIGN:
			pgp_mpi_hash(fc_md, fc_pk->sec.rsa.d);
			pgp_mpi_hash(fc_md, fc_pk->sec.rsa.p);
			pgp_mpi_hash(fc_md, fc_pk->sec.rsa.q);
			pgp_mpi_hash(fc_md, fc_pk->sec.rsa.u);
			break;
		case PGP_PUB_DSA_SIGN:
			pgp_mpi_hash(fc_md, fc_pk->sec.dsa.x);
			break;
	}
	px_md_finish(fc_md, fc_my_sha1);
	px_md_free(fc_md);

	if (memcmp(fc_my_sha1, fc_got_sha1, 20) != 0)
	{
		px_debug("key sha1 check failed");
		fc_res = PXE_PGP_KEYPKT_CORRUPT;
	}
err:
	px_memset(fc_got_sha1, 0, 20);
	px_memset(fc_my_sha1, 0, 20);
	return fc_res;
}

static int fc_check_key_cksum(PullFilter *fc_src, PGP_PubKey *fc_pk)
{
	int			fc_res;
	unsigned	fc_got_cksum,
				fc_my_cksum = 0;
	uint8		fc_buf[2];

	fc_res = pullf_read_fixed(fc_src, 2, fc_buf);
	if (fc_res < 0)
		return fc_res;

	fc_got_cksum = ((unsigned) fc_buf[0] << 8) + fc_buf[1];
	switch (fc_pk->algo)
	{
		case PGP_PUB_ELG_ENCRYPT:
			fc_my_cksum = pgp_mpi_cksum(0, fc_pk->sec.elg.x);
			break;
		case PGP_PUB_RSA_SIGN:
		case PGP_PUB_RSA_ENCRYPT:
		case PGP_PUB_RSA_ENCRYPT_SIGN:
			fc_my_cksum = pgp_mpi_cksum(0, fc_pk->sec.rsa.d);
			fc_my_cksum = pgp_mpi_cksum(fc_my_cksum, fc_pk->sec.rsa.p);
			fc_my_cksum = pgp_mpi_cksum(fc_my_cksum, fc_pk->sec.rsa.q);
			fc_my_cksum = pgp_mpi_cksum(fc_my_cksum, fc_pk->sec.rsa.u);
			break;
		case PGP_PUB_DSA_SIGN:
			fc_my_cksum = pgp_mpi_cksum(0, fc_pk->sec.dsa.x);
			break;
	}
	if (fc_my_cksum != fc_got_cksum)
	{
		px_debug("key cksum check failed");
		return PXE_PGP_KEYPKT_CORRUPT;
	}
	return 0;
}

static int fc_process_secret_key(PullFilter *fc_pkt, PGP_PubKey **fc_pk_p,
				   const uint8 *fc_key, int fc_key_len)
{
	int			fc_res;
	int			fc_hide_type;
	int			fc_cipher_algo;
	int			fc_bs;
	uint8		fc_iv[512];
	PullFilter *fc_pf_decrypt = NULL,
			   *fc_pf_key;
	PGP_CFB    *fc_cfb = NULL;
	PGP_S2K		fc_s2k;
	PGP_PubKey *fc_pk;

	/* 首先读取公钥部分 */
	fc_res = _pgp_read_public_key(fc_pkt, &fc_pk);
	if (fc_res < 0)
		return fc_res;

	/*
	 * 秘钥是否被加密?
	 */
	GETBYTE(fc_pkt, fc_hide_type);
	if (fc_hide_type == HIDE_SHA1 || fc_hide_type == HIDE_CKSUM)
	{
		if (fc_key == NULL)
			return PXE_PGP_NEED_SECRET_PSW;
		GETBYTE(fc_pkt, fc_cipher_algo);
		fc_res = pgp_s2k_read(fc_pkt, &fc_s2k);
		if (fc_res < 0)
			return fc_res;

		fc_res = pgp_s2k_process(&fc_s2k, fc_cipher_algo, fc_key, fc_key_len);
		if (fc_res < 0)
			return fc_res;

		fc_bs = pgp_get_cipher_block_size(fc_cipher_algo);
		if (fc_bs == 0)
		{
			px_debug("unknown cipher algo=%d", fc_cipher_algo);
			return PXE_PGP_UNSUPPORTED_CIPHER;
		}
		fc_res = pullf_read_fixed(fc_pkt, fc_bs, fc_iv);
		if (fc_res < 0)
			return fc_res;

		/*
		 * 创建解密过滤器
		 */
		fc_res = pgp_cfb_create(&fc_cfb, fc_cipher_algo, fc_s2k.key, fc_s2k.key_len, 0, fc_iv);
		if (fc_res < 0)
			return fc_res;
		fc_res = pullf_create(&fc_pf_decrypt, &pgp_decrypt_filter, fc_cfb, fc_pkt);
		if (fc_res < 0)
			return fc_res;
		fc_pf_key = fc_pf_decrypt;
	}
	else if (fc_hide_type == HIDE_CLEAR)
	{
		fc_pf_key = fc_pkt;
	}
	else
	{
		px_debug("unknown hide type");
		return PXE_PGP_KEYPKT_CORRUPT;
	}

	/* 读取秘钥 */
	switch (fc_pk->algo)
	{
		case PGP_PUB_RSA_SIGN:
		case PGP_PUB_RSA_ENCRYPT:
		case PGP_PUB_RSA_ENCRYPT_SIGN:
			fc_res = pgp_mpi_read(fc_pf_key, &fc_pk->sec.rsa.d);
			if (fc_res < 0)
				break;
			fc_res = pgp_mpi_read(fc_pf_key, &fc_pk->sec.rsa.p);
			if (fc_res < 0)
				break;
			fc_res = pgp_mpi_read(fc_pf_key, &fc_pk->sec.rsa.q);
			if (fc_res < 0)
				break;
			fc_res = pgp_mpi_read(fc_pf_key, &fc_pk->sec.rsa.u);
			if (fc_res < 0)
				break;
			break;
		case PGP_PUB_ELG_ENCRYPT:
			fc_res = pgp_mpi_read(fc_pf_key, &fc_pk->sec.elg.x);
			break;
		case PGP_PUB_DSA_SIGN:
			fc_res = pgp_mpi_read(fc_pf_key, &fc_pk->sec.dsa.x);
			break;
		default:
			px_debug("unknown public algo: %d", fc_pk->algo);
			fc_res = PXE_PGP_KEYPKT_CORRUPT;
	}
	/* 读取校验和 / sha1 */
	if (fc_res >= 0)
	{
		if (fc_hide_type == HIDE_SHA1)
			fc_res = fc_check_key_sha1(fc_pf_key, fc_pk);
		else
			fc_res = fc_check_key_cksum(fc_pf_key, fc_pk);
	}
	if (fc_res >= 0)
		fc_res = pgp_expect_packet_end(fc_pf_key);

	if (fc_pf_decrypt)
		pullf_free(fc_pf_decrypt);
	if (fc_cfb)
		pgp_cfb_free(fc_cfb);

	if (fc_res < 0)
		pgp_key_free(fc_pk);
	else
		*fc_pk_p = fc_pk;

	return fc_res;
}

static int fc_internal_read_key(PullFilter *fc_src, PGP_PubKey **fc_pk_p,
				  const uint8 *fc_psw, int fc_psw_len, int fc_pubtype)
{
	PullFilter *fc_pkt = NULL;
	int			fc_res;
	uint8		fc_tag;
	int			fc_len;
	PGP_PubKey *fc_enc_key = NULL;
	PGP_PubKey *fc_pk = NULL;
	int			fc_got_main_key = 0;

	/*
	 * 查找加密密钥。
	 *
	 * 对任何复杂的情况返回错误。
	 */
	while (1)
	{
		fc_res = pgp_parse_pkt_hdr(fc_src, &fc_tag, &fc_len, 0);
		if (fc_res <= 0)
			break;
		fc_res = pgp_create_pkt_reader(&fc_pkt, fc_src, fc_len, fc_res, NULL);
		if (fc_res < 0)
			break;

		switch (fc_tag)
		{
			case PGP_PKT_PUBLIC_KEY:
			case PGP_PKT_SECRET_KEY:
				if (fc_got_main_key)
				{
					fc_res = PXE_PGP_MULTIPLE_KEYS;
					break;
				}
				fc_got_main_key = 1;
				fc_res = pgp_skip_packet(fc_pkt);
				break;

			case PGP_PKT_PUBLIC_SUBKEY:
				if (fc_pubtype != 0)
					fc_res = PXE_PGP_EXPECT_SECRET_KEY;
				else
					fc_res = _pgp_read_public_key(fc_pkt, &fc_pk);
				break;

			case PGP_PKT_SECRET_SUBKEY:
				if (fc_pubtype != 1)
					fc_res = PXE_PGP_EXPECT_PUBLIC_KEY;
				else
					fc_res = fc_process_secret_key(fc_pkt, &fc_pk, fc_psw, fc_psw_len);
				break;

			case PGP_PKT_SIGNATURE:
			case PGP_PKT_MARKER:
			case PGP_PKT_TRUST:
			case PGP_PKT_USER_ID:
			case PGP_PKT_USER_ATTR:
			case PGP_PKT_PRIV_61:
				fc_res = pgp_skip_packet(fc_pkt);
				break;
			default:
				px_debug("unknown/unexpected packet: %d", fc_tag);
				fc_res = PXE_PGP_UNEXPECTED_PKT;
		}
		pullf_free(fc_pkt);
		fc_pkt = NULL;

		if (fc_pk != NULL)
		{
			if (fc_res >= 0 && fc_pk->can_encrypt)
			{
				if (fc_enc_key == NULL)
				{
					fc_enc_key = fc_pk;
					fc_pk = NULL;
				}
				else
					fc_res = PXE_PGP_MULTIPLE_SUBKEYS;
			}

			if (fc_pk)
				pgp_key_free(fc_pk);
			fc_pk = NULL;
		}

		if (fc_res < 0)
			break;
	}

	if (fc_pkt)
		pullf_free(fc_pkt);

	if (fc_res < 0)
	{
		if (fc_enc_key)
			pgp_key_free(fc_enc_key);
		return fc_res;
	}

	if (!fc_enc_key)
		fc_res = PXE_PGP_NO_USABLE_KEY;
	else
		*fc_pk_p = fc_enc_key;
	return fc_res;
}

int pgp_set_pubkey(PGP_Context *fc_ctx, MBuf *fc_keypkt,
			   const uint8 *fc_key, int fc_key_len, int fc_pubtype)
{
	int			fc_res;
	PullFilter *fc_src;
	PGP_PubKey *fc_pk = NULL;

	fc_res = pullf_create_mbuf_reader(&fc_src, fc_keypkt);
	if (fc_res < 0)
		return fc_res;

	fc_res = fc_internal_read_key(fc_src, &fc_pk, fc_key, fc_key_len, fc_pubtype);
	pullf_free(fc_src);

	if (fc_res >= 0)
		fc_ctx->pub_key = fc_pk;

	return fc_res < 0 ? fc_res : 0;
}
